package com.shaozi.nc.manage;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;

import javax.annotation.PostConstruct;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import com.shaozi.data.mq.business.base.MqConsts;
import com.shaozi.data.mq.business.base.SourceTypeConsts;
import com.shaozi.data.mq.business.bean.MQExchangeData;
import com.shaozi.data.mq.business.bean.MQExchangeRuleBean;
import com.shaozi.data.mq.business.bean.MQSyncItem;
import com.shaozi.lib.concurrent.DefaultLocker;
import com.shaozi.lib.manage.ManageBase;
import com.shaozi.lib.observer.CommonObservable;
import com.shaozi.lib.request.BaseRequest;
import com.shaozi.lib.request.RequestCallback;
import com.shaozi.lib.throwable.SzException;
import com.shaozi.nc.bean.companyInfo.CompanyInfoUpdateBizBean;
import com.shaozi.nc.data.model.CompanyInfoModel;
import com.shaozi.nc.data.model.UserInfoModel;
import com.shaozi.nc.data.mongodao.CompanyInfoMDao;
import com.shaozi.nc.manage.bean.CheckUpdateCompany;
import com.shaozi.nc.manage.bean.CheckUpdateUserInfo;
import com.shaozi.nc.mq.producer.SendIMInfoProducer;
import com.shaozi.utils.common.BeanUtils;

@Component
public class CompanyInfoManager extends ManageBase {
	private final Logger logger = Logger.getLogger(CompanyInfoManager.class);

	private Map<String, Integer> updateLock;
	private DefaultLocker<String> companyIdLocker;
	@Autowired
	private BaseRequest userRequest;
	/**
	 * compId =>
	 */
	private Map<String, CompanyInfoModel> companyInfoMap;

	@Autowired
	protected CompanyUserInfoManage companyUserInfoManger;

	@Autowired
	private CompanyInfoMDao companyUserInfoMDao;

	@Autowired
	@Qualifier("companyObservable")
	private CommonObservable companyObservable;
	private Semaphore startupSem;

	public CompanyInfoManager() {
		updateLock = new ConcurrentHashMap<String, Integer>();
		companyIdLocker = new DefaultLocker<String>();
		companyInfoMap = new ConcurrentHashMap<String, CompanyInfoModel>();
		startupSem = new Semaphore(0);
	}

	public CompanyInfoModel getCompanyInfo(String companyId) {
		CompanyInfoModel companyInfoModel = companyInfoMap.get(companyId);
		if (companyInfoModel == null) {
			companyInfoModel = companyUserInfoMDao.getCompanyInfo(companyId);
			if (companyInfoModel != null) {
				companyInfoMap.put(companyId, companyInfoModel);
			}
		}
		return companyInfoModel;
	}

	public Set<String> getCompanyIds() {
		return companyInfoMap.keySet();
	}

	/**
	 * 加载公司id,等待启动
	 * 
	 * @return
	 */
	public Set<String> getCompanyIdsWaitStartup() {
		if (startupSem.availablePermits() == 0) {
			try {
				startupSem.acquire();
				startupSem.release();
			} catch (InterruptedException e) {
				logger.error(SzException.getStackMessage(e));
				return null;
			}
		}
		return getCompanyIds();
	}
	@PostConstruct
	public void loadAsync() {
		new Thread(new Loader()).start();
	}

	private class Loader implements Runnable {

		@Override
		public void run() {
			try {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
				}
				loadAndNotify();
			} finally {
				startupSem.release();
				logger.info("notify company end in nc");
			}
		}
	}

	public void saveCompanyInfo(CompanyInfoModel companyInfo, boolean saveToDb) {

		CompanyInfoModel storeCompanyInfo = companyInfoMap.get(companyInfo.getCompanyId());
		if (storeCompanyInfo == null) {
			companyInfoMap.put(companyInfo.getCompanyId(), companyInfo);
			storeCompanyInfo = companyInfo;
			storeCompanyInfo.setCreateTime(new Date(System.currentTimeMillis()));
		} else {
			if (StringUtils.isEmpty(companyInfo.getCompanyId())) {
				logger.error(String.format("companyId null! companyName is %s", companyInfo.getCompanyName()));
				return;
			}
			if (StringUtils.isEmpty(companyInfo.getCompanyDomain())) {
				storeCompanyInfo.setCompanyDomain(companyInfo.getCompanyDomain());
			}
			if (StringUtils.isEmpty(companyInfo.getCompanyName())) {
				storeCompanyInfo.setCompanyName(companyInfo.getCompanyName());
			}
			if (companyInfo.getLastUpdateTime() != 0) {
				storeCompanyInfo.setLastUpdateTime(companyInfo.getLastUpdateTime());
			}
		}

		if (saveToDb)
			companyUserInfoMDao.upsertCompanyInfo(storeCompanyInfo);
		if (companyObservable != null) {
			companyObservable.notifyObservers(companyInfo);
		}
	}

	public CompletableFuture<Void> updateCompanyInfo(String companyId, boolean saveToDb) {
		if (StringUtils.isEmpty(companyId)) {
			logger.debug("companyId is null");
			return null;
		}
		companyIdLocker.lock("update.company" + companyId); // 用字符串内容可以作为锁的依据
		try {
			int changeIdentity = updateLock.get(companyId) != null ? updateLock.get(companyId) : 0;
			updateLock.put(companyId, changeIdentity + 1);
			if (changeIdentity == 0) {
				// 如果更新时间为空，说明并没有正在更新该公司的信息，如果存储的更新时间小于当前要更新的时间，说明需要继续更新
				changeIdentity++;
				// 现在没有更新当前公司的信息时进行更新，否则等待更新结果回来再判断是否需要更新
				CompanyInfoModel oldInfo = getCompanyInfo(companyId);
				final long identity = oldInfo == null ? 0l : oldInfo.getLastUpdateTime();

				CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
					final CountDownLatch latch = new CountDownLatch(1);
					userRequest.doRequest(new RequestCallback() {
						@Override
						public void callback(boolean isSuccess, Object obj) {
							try {
								if (isSuccess) {
									CheckUpdateCompany updateCompany = (CheckUpdateCompany) obj;
									handleUpdateCompanyInfoResponse(companyId, updateCompany, saveToDb, identity);
								}
							} finally {
								latch.countDown();
							}
						}
					}, companyId, identity);
					try {
						latch.await();
					} catch (Exception e) {
					}
				});
				return future;
			}
		} catch (Exception e) {
			logger.error(SzException.getStackMessage(e));
		} finally {
			companyIdLocker.unlock("update.company" + companyId);
		}
		return null;
	}

	private void handleUpdateCompanyInfoResponse(String companyId, CheckUpdateCompany updateCompany, boolean saveToDb,
			long identity_log) {
		if (!isCheckUpdateCompanyEmpty(updateCompany)) {
			// 如果oldInfo为空，那么说明是新建一个公司，lastUpdateTime的值是0去请求服务
			if (updateLock.get(companyId) > 1) {
				// 如果存储的更新时间大于本次更新返回的时间，说明还需要更新
				updateLock.put(companyId, 0);
				updateCompanyInfo(companyId, saveToDb);
			} else {
				updateLock.put(companyId, 0);
				storeUserInfoChange(updateCompany.getInsert(), updateCompany.getId());
				storeUserInfoChange(updateCompany.getUpdate(), updateCompany.getId());
				signUsertInfoDeleted(updateCompany.getDelete(), updateCompany.getId());

				CompanyInfoModel info = new CompanyInfoModel();
				info.setCompanyId(updateCompany.getId());
				info.setCompanyName(updateCompany.getCompany_name());
				info.setLastUpdateTime(updateCompany.getMax_identity());
				logger.info("update companyId=" + companyId + ";name=" + updateCompany.getCompany_name());
				saveCompanyInfo(info, saveToDb);
				updateCompanySendMQ(companyId);
			}
		}
	}

	/**
	 * 标记删除
	 */
	private void signUsertInfoDeleted(List<String> deleteList, String companyId) {
		if (deleteList != null) {
			companyUserInfoManger.signUserInfoDeleted(companyId, deleteList);
		}
	}

	private void updateCompanySendMQ(String companyId) {
		SendIMInfoProducer producer = (SendIMInfoProducer) BeanUtils.getBean(SendIMInfoProducer.class);
		MQExchangeData<MQExchangeRuleBean<CompanyInfoUpdateBizBean>> data = new MQExchangeData<MQExchangeRuleBean<CompanyInfoUpdateBizBean>>();
		MQExchangeRuleBean<CompanyInfoUpdateBizBean> rule = new MQExchangeRuleBean<CompanyInfoUpdateBizBean>();
		data.setRule(rule);
		data.setAction_type(MqConsts.ActionType.Sync);
		data.setCompanyId(companyId);

		MQSyncItem sync = new MQSyncItem();
		sync.setSourceType(SourceTypeConsts.Sync.CompanyUserInfo.CompanyUserInfoNotify);
		rule.setSync(sync);

		CompanyInfoUpdateBizBean bizBean = new CompanyInfoUpdateBizBean();
		bizBean.setCompanyId(companyId);
		bizBean.setIdentity(new long[] { 1, 0, 0, 0 });
		rule.setBiz(bizBean);
		producer.push(data);
	}

	private boolean isCheckUpdateCompanyEmpty(CheckUpdateCompany updateCompany) {
		if (updateCompany == null)
			return true;
		if (StringUtils.isEmpty(updateCompany.getId()))
			return true;
		return false;
	}

	/**
	 * 存储更改或者新建的
	 */
	private void storeUserInfoChange(List<CheckUpdateUserInfo> list, String companyId) {
		if (list != null) {
			for (CheckUpdateUserInfo checkUserInfo : list) {
				UserInfoModel userInfo = companyUserInfoManger.getUserInfo(companyId, checkUserInfo.getId());
				if (userInfo != null) {
					userInfo.setUsername(checkUserInfo.getUsername());
				} else {
					userInfo = new UserInfoModel();
					userInfo.setCompanyId(companyId);
					userInfo.setUid(checkUserInfo.getId());
					userInfo.setUsername(checkUserInfo.getUsername());
				}
				userInfo.setIsDel(UserInfoModel.USERINFO_EXIST);
				companyUserInfoManger.updateUserInfo(companyId, userInfo);
			}
		}
	}

	private void loadAndNotify() {
		StringBuffer sb = new StringBuffer();
		List<CompanyInfoModel> companyInfos = companyUserInfoMDao.getCompanyInfos();
		sb.append("load company size=").append(companyInfos.size()).append(";[");
		companyInfos.forEach((model) -> {
			sb.append(model.getCompanyId()).append(",");
			companyInfoMap.put(model.getCompanyId(), model);
			if (companyObservable != null) {
				companyObservable.notifyObservers(model);
			}
		});
		sb.append("]");
		logger.info(sb.toString());
	}

}
